package com.liuym.common.redis;

import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;

import java.io.UnsupportedEncodingException;

/**
 * redis锁工具类
 */
public class RedisLockUtil {
    // 编码
    private static String redisCode = "utf-8";

    /**
     * @param redisTemplate
     * @param lockKey
     * @param value
     * @param expireSeconds 锁过期时间，如果小于1s，则取3600s
     * @return boolean
     * @throws
     * @Title: acquire
     * @Description: 基于redis的分布式锁，参数expireSeconds，类似于定时器，在expireSeconds设置的时间范围内，任何进程都拿不到锁
     */
    public static boolean acquire(RedisTemplate<String, String> redisTemplate, String lockKey, String value, long expireSeconds) {
        if (expireSeconds < 1) {
            expireSeconds = 3600;
        }
        // redis始终保证只有一个进程能设值成功，这样保证只有一个进程能拿到锁
        boolean result = setNX(redisTemplate, lockKey.getBytes(), value.getBytes(), expireSeconds);
        return result;
    }

    /**
     * @param redisTemplate
     * @param lockKey
     * @param expireSeconds 锁过期时间，如果小于1s，则取3600s
     * @return boolean
     * @throws
     * @Title: acquire
     * @Description: 基于redis的分布式锁，参数expireSeconds，类似于定时器，在expireSeconds设置的时间范围内，任何进程都拿不到锁
     */
    public static boolean acquire(RedisTemplate<String, String> redisTemplate, String lockKey, long expireSeconds) {
        if (expireSeconds < 1) {
            expireSeconds = 3600;
        }
        // 设置key的值，为过期时间的值
        String expiresStr = String.valueOf(expireSeconds);
        // redis始终保证只有一个进程能设值成功，这样保证只有一个进程能拿到锁
        boolean result = setNX(redisTemplate, lockKey.getBytes(), expiresStr.getBytes(), expireSeconds);
        return result;
    }

    private static boolean setNX(RedisTemplate<String, String> redisTemplate, final byte[] key, final byte[] value, final long expires) {
        return redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                if (connection.setNX(key, value)) {
                    return connection.expire(key, expires);
                } else {
                    return false;
                }
            }
        });
    }

    /**
     * @param redisTemplate
     * @param lockKey
     * @return void
     * @throws
     * @Title: release
     * @Description: 释放锁
     */
    public static void release(RedisTemplate<String, String> redisTemplate, String lockKey) {
        byte[] key = lockKey.getBytes();
        del(redisTemplate, key);
    }

    private static void del(RedisTemplate<String, String> redisTemplate, final byte[] key) {
        redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                connection.del(key);
                return 1L;
            }
        });
    }

    /**
     * @param redisTemplate
     * @param lockKey
     * @return String
     * @throws UnsupportedEncodingException ， Exception
     * @Title: getKey
     * @Description: 获取redis key
     */
    public static String getKey(RedisTemplate<String, String> redisTemplate, String lockKey) {
        byte[] key = lockKey.getBytes();
        return get(redisTemplate, key);
    }

    private static String get(RedisTemplate<String, String> redisTemplate, final byte[] key) {
        return redisTemplate.execute(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                try {
                    return new String(connection.get(key), redisCode);
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return "";
            }
        });
    }

}
